package seatiger.tomcat.coyote.http11;

import seatiger.tomcat.catalina.Constants;
import seatiger.tomcat.catalina.connector.http.SocketInputStream;
import seatiger.tomcat.coyote.InputBuffer;
import seatiger.tomcat.coyote.OutputBuffer;
import seatiger.tomcat.coyote.Request;
import seatiger.tomcat.util.StringManager;
import seatiger.tomcat.util.buf.ByteChunk;
import seatiger.tomcat.util.buf.MessageBytes;
import seatiger.tomcat.util.http.MimeHeaders;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;

/**
 * 套接字 字节缓冲装置，负责读取解析http协议，提供缓冲
 */
public class InternalInputBuffer implements InputBuffer {

    /**
     * 存放缓冲区的 字节流
     */
    protected byte[] buf;

    /**
     * 读取指针,代表buf中当前读取的位置
     */
    protected int pos;

    /**
     * 从流中读取数据，填充到buf中最后的位置
     */
    protected int lastValid;

    /**
     * 表示缓冲区报文头部结束的位置，也是报文体开始的位置
     */
    protected int end;

    /**
     * 底层的输入流
     */
    protected InputStream inputStream;

    /**
     * State. 是否解析头部的标志
     */
    protected boolean parsingHeader;

    protected static StringManager sm = StringManager.getManager(Constants.PACKAGE_HTTP11);

    //关联的coyote request
    protected Request request;

    protected MimeHeaders headers;

    /**
     * input 过滤器库
     */
    protected InputFilter[] filterLibrary;

    /**
     * 每次请求需要使用的过滤器。（已经排好序了）
     */
    protected InputFilter[] activeFilters;

    /**
     * 最后一个活动的过滤器索引（就是从activeFilters中选一个开始执行这个过滤器）
     */
    protected int lastActiveFilter;

    /**
     * 封装的底层数据流，真正读取数据的流
     */
    protected InputBuffer inputStreamInputBuffer;

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    /**
     * @param request          coyote request 对象，封装了字节数据组
     * @param headerBufferSize 接收报文头字节数组的大小
     */
    public InternalInputBuffer(Request request, int headerBufferSize) {
        buf = new byte[headerBufferSize];
        parsingHeader = true;
        this.request = request;
        this.headers = request.getMimeHeaders();
        this.lastActiveFilter = -1;
        inputStreamInputBuffer = new InputStreamInputBuffer();
        filterLibrary = new InputFilter[0];
        activeFilters = new InputFilter[0];
    }

    /**
     * 将过滤器添加到库中
     * @param filter
     */
    public void addFilter(InputFilter filter) {
        InputFilter[] newFilterLibrary =
                new InputFilter[filterLibrary.length + 1];
        for (int i = 0; i < filterLibrary.length; i++) {
            newFilterLibrary[i] = filterLibrary[i];
        }
        newFilterLibrary[filterLibrary.length] = filter;
        filterLibrary = newFilterLibrary;
        activeFilters = new InputFilter[filterLibrary.length];
    }

    /**
     * 添加活动的过滤器
     * @param filter
     */
    public void addActiveFilter(InputFilter filter) {
        if (lastActiveFilter == -1) {
            filter.setBuffer(inputStreamInputBuffer);
        } else {
            for(int i = 0; i < activeFilters.length ;i++) {
                //已经存在了
                if (activeFilters[i] == filter) {
                    return;
                }
            }
            //将已经存在filter 链接到当前的上面
            filter.setBuffer(activeFilters[lastActiveFilter]);
        }
        //将当前的filter作为最新的，到时候执行就是从这个filter开始执行
        activeFilters[++lastActiveFilter] = filter;
        filter.setRequest(request);
    }

    /**
     * 获取所有库中的filter
     * @return
     */
    public InputFilter[] getFilters() {
        return filterLibrary;
    }

    /**
     * 解析http的请求行，不会尝试解析报文体中的内容
     */
    public void parseRequestLine() throws IOException {
        int start = 0;
        byte chr = 0;

        //1 将请求行中开始位置的回车换行过滤掉
        do {
            //已经读到buf末尾的位置，需要重新从流中读取数据
            if (pos >= lastValid) {
                if (!fill()) {
                    throw new EOFException(sm.getString("iib.eof.error"));
                }
            }
            chr = buf[pos++];
        } while (chr == Constants.CR || chr == Constants.LF);
        pos--;
        //2 从请求行中解析出请求方法
        //标记当前读取到buf中的位置
        start = pos;
        boolean space = false;
        while (!space) {
            if (pos >= lastValid) {
                if (!fill()) {
                    throw new EOFException(sm.getString("iib.eof.error"));
                }
            }
            //请求方法中的位置
            if (buf[pos] == Constants.SP) {
                space = true;
                request.method().setBytes(buf, start, pos - start);
            }
            pos++;
        }
        //3 解析出URI
        start = pos;
        space = false;
        int questionPos = -1;
        while (!space) {
            if (pos >= lastValid) {
                if (!fill()) {
                    throw new EOFException(sm.getString("iib.eof.error"));
                }
            }
            //3.1 空格 表示URL
            if (buf[pos] == Constants.SP) {
                space = true;
                request.unparsedURI().setBytes(buf, start, pos - start);
            } else if (buf[pos] == Constants.QUESTION && questionPos == -1) { //3.2 解析出? 请求参数
                questionPos = pos;
            }
            pos++;
        }
        if (questionPos >= 0) {
            request.queryString().setBytes(buf, questionPos + 1, (pos - 1) - (questionPos + 1));
            request.requestURI().setBytes(buf, start, questionPos - start);
        } else {
            request.requestURI().setBytes(buf, start, (pos - 1) - start);
        }
        //4 读取协议
        start = pos;
        int protocolEnd = 0;
        boolean eol = false;
        while (!eol) {
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.CR) {
                protocolEnd = pos;
            } else if (buf[pos] == Constants.LF) {
                protocolEnd = protocolEnd == 0 ? pos : protocolEnd;
                eol = true;
                request.protocol().setBytes(buf, start, pos - start);
            }
            pos++;
        }
    }

    /**
     * 解析请求头
     */
    public void parseHeaders() throws IOException {
        while (parseHeader()) {
        }
        parsingHeader = false;
        //end就是报文体中的内容
        end = pos;
    }

    /**
     * 解析报文头
     *
     * @return 如果返回false表明报文头已经解析完成了
     */
    public boolean parseHeader() throws IOException {
        byte chr;
        //1 校验报文头是否解析完成
        while (true) {
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            chr = buf[pos];
            //如果是回车换行，表示报文头解析完成
            if (chr == Constants.CR || chr == Constants.LF) {
                if (chr == Constants.LF) {
                    pos++;
                    //报文头解析完成
                    return false;
                }
            } else {
                break;
            }
            pos++;
        }
        //2 解析headName
        int start = pos;
        // :
        boolean colon = false;
        MessageBytes headerValue = null;
        while (!colon) {
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.COLON) {
                colon = true;
                headerValue = headers.addValue(buf, start, pos - start);
            }
            chr = buf[pos];
            if (chr >= Constants.A && chr <= Constants.Z) {
                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
            }
            pos++;
        }
        //2 解析headValue
        boolean eol = false;
        boolean space = true;
        //过滤掉headValue 前面的空格和tab
        while (space) {
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;
            } else {
                space = false;
            }
        }
        start = pos;
        while (!eol) {
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.CR) {//回车，不做处理
            } else if (buf[pos] == Constants.LF) { //换行,一个请求头处理完成
                eol = true;
                headerValue.setBytes(buf, start, pos - start -1);
            }
            pos++;
        }
        return true;
    }


    /**
     * 从底层流中读取数据 填充buf
     * 解析请求行+请求头 ，报文的长度不能超过 8* 1024个字节
     *
     * @return false 表示流结束了
     */
    public boolean fill() throws IOException {
        if (parsingHeader) {
            if (lastValid == buf.length) {
                if (lastValid == buf.length) {
                    throw new IOException(sm.getString("iib.requestheadertoolarge.error"));
                }
            }
            int nRead = 0;
            pos = end;
            lastValid = pos;
            //从底层流中的数据读入buf中，开始位置是 pos的位置，读取的长度 是buf中剩余的位置，就是（总长度 - lastValid）
            nRead = inputStream.read(buf, pos, buf.length - lastValid);
            if (nRead > 0) {
                lastValid = pos + nRead;
            }
            return nRead > 0;
        }//TODO
        return false;
    }

    /**
     * 从报文体中读取数据 到字节块中
     * @param chunk
     * @param request
     * @return
     * @throws IOException
     */
    @Override
    public int doRead(ByteChunk chunk, Request request) throws IOException {
        if (lastActiveFilter == -1) {
            return inputStreamInputBuffer.doRead(chunk,request);
        }else {
            return activeFilters[lastActiveFilter].doRead(chunk,request);
        }
    }

    /**
     * 把流中多余的数据消费掉
     */
    public void endRequest() throws IOException{
        if (lastActiveFilter != -1) {
            int extraBytes = (int)activeFilters[lastActiveFilter].end();
            pos = pos - extraBytes;
        }
    }

    /**
     * 真正用于底层读取数据的buffer
     */
    protected class InputStreamInputBuffer implements InputBuffer {
        @Override
        public int doRead(ByteChunk chunk, Request request) throws IOException {
            if (pos >= lastValid) {
                if (!fill()){
                    return -1;
                }
            }
            int length = lastValid - pos;
            chunk.setBytes(buf, pos, length);
            pos = lastValid;
            return length;
        }
    }

    /**
     * 处理下一个请求
     */
    public void nextRequest() {
        request.recycle();
        //TODO 判断有没有下一个请求
        // Recycle filters
        for (int i = 0; i <= lastActiveFilter; i++) {
            activeFilters[i].recycle();
        }
        lastActiveFilter = -1;
        lastValid = 0;
        pos = 0;
        parsingHeader = true;
    }
}
